{------------------------------------------------------------------------------}
{- Text File  : UNIXTIME.TXT                                                  -}
{- Programmer : Todd Fiske                                                    -}
{-                                                                            -}
{- Purpose    : Notes about UNIXTIME.PAS                                      -}
{-                                                                            -}
{- Revision   : 01/16/1995 - first version                                    -}
{- History      05/16/1996 - updated for bug-fix upload                       -}
{-                                                                            -}
{- Language   : Turbo Pascal 7.0                                              -}
{-                                                                            -}
{------------------------------------------------------------------------------}

UNIXTIME.PAS is a unit that implements a few routines for converting Unix time
values into Borland/Turbo Pascal DateTime records and back. The routines are:

   procedure UnpackUnixTime     (t : longint; var dt : DateTime);
   procedure PackUnixTime       (dt : DateTime; var t : longint);
   function  FormatUnixTime     (t : longint) : string;

UnpackUnixTime converts a Unix time longint into a Borland/Turbo Pascal
DateTime record.

   832268074 ==> 05/16/1996 17:34:34

PackUnixTime goes the other way of course.

   05/16/1996 17:34:34 ==> 832268074

FormatUnixTime creates a string in this format from a Unix time longint:

   832268074 ==> Thu May 16 17:34:34 1996

{------------------------------------------------------------------------------}

This unit also contains a few "general-purpose" routines from my string unit:

   function  int2str            (i : longint; l : integer) : string_10;
   function  int2dt             (i : longint) : string_2;
   function  str2int            (s : string) : longint;

Int2str converts a longint into a string of length specified with the second
parameter. Use a length of 1 to create non-justified strings.

Int2dt converts a longint into a 2-character zero-filled string, particularly
useful for converting time and date components into strings.

Str2int converts a string into an integer value if possible. If the input
string doesn't make sense numerically, a longint of value 0 is returned.

(Side note: the Borland/Turbo Pascal Val() procedure (which str2int is based
on) can convert "hex" format strings into numbers, eg str2int('$100') will
return 256.)

{------------------------------------------------------------------------------}

A Unix time longint is a number of seconds from January 1 1970 at midnight
(beginning of the day). I first became involved with these when downloading
Usenet newsgroup articles in bulk from the Internet. Being most familiar with
CompuServe forum messages and the orderly way in which they are presented, I
sought to write a program to index Usenet messages in a similar thread oriented
fashion. I never completed the indexing program due the vast flexibility of the
Usenet messaging scheme, but boy did I learn a few things about Unix in the
meantime! =)

These routines are based on a corresponding C function that I found in some
gomby huge archive on the Internet, I think as part of the PC-TAR source code.
My first attempt at directly converting that C routine failed miserably, so I
ditched that and started over from scratch, just implementing the "high-level
psuedo-code" version of the algorithm: the number of seconds since 01/01/1970.
The resulting routines seem to work correctly, although I haven't been able to
prove them with anything more substantial than doing the process myself
longhand on paper. Either I'm making the exact same mistakes every time, or the
routines work! =)

[I *was* making the same mistake everytime! YIKES!!]

There are 86,400 seconds in a day (60 seconds * 60 minutes * 24 hours),
31,536,000 seconds in a normal year, and 31,622,400 seconds in a leap year.
Right now as I write this, it's 832,268,074 seconds since 01/01/1970 (ie,
05/16/1996 17:34:34). A longint can store a value as large as 4,294,967,295,
given that half of those numbers will appear to be negative. This means a Unix
time longint can store a little over 130 years worth of seconds. Since its been
25 years since the beginning of time accoring to Unix, we've only got a little
over 100 years left. Hopefully by then there will be a new system in place! =)

However, since Pascal 32-bit values are signed, we can only effectively work
with values up to 2,147,483,647. This is Tue Jan 19 03:14:07 2038, which gives
me 43 years to get a version finished that works with coprocessor emulation and
the Comp type - jeesh I hate deadlines! <g>

{------------------------------------------------------------------------------}

The first release of this package contained two bugs: firstly, the constant
secs_per_hour was given the value 3660, which should have been 3600.

Secondly, the PackUnixTime routine added an extra day to dates in a leap year
if the date was before the leap day. For example, 02/29/1996 and 03/01/1996
converted correctly, but dates from 01/01/1996 through 02/28/1996 inclusive
included an extra day's worth of seconds (86,400).

The solution to this was to back a day out of the value num_leap_years (which
actually should be called num_leap_days) if the target year is a leap year.
This works because the extra day is already added to the days_per_month[1]
array slot (1 being the 0-based index for February) if the target year is a
leap year.

{------------------------------------------------------------------------------}

I would like to thank Michael Bostic for making me aware of these bugs in the
initial version. Another person also provided some bug reports, but
unfortunately I have lost his address. He had also put together a Delphi unit
(which now needs to be updated <g>) encapsulating these routines. I thought the
unit was available in the Delphi (BPascal) Forum in CompuServe, but now I can't
find it. If you're reading this guy, get in touch so I can thank you properly!

{------------------------------------------------------------------------------}

This package also contains a few utility and test programs:

UTTEST.PAS is a simple command line test program. It takes a Unix time longint
on the command line, converts it to a DateTime record, displays that, and
converts the DateTime record back into a longint, and displays that. This is
designed to verify that the Unpack- and Pack- routines work correctly.


DT2UT.PAS takes a date on the command line in "normal" format and displays the
corresponding Unix time longint. DT2UT can now handle either 4-digit or 2-digit
year values, ie, with or without the century. It follows this logic to
determine what century you want:

   if year < 100 then                  if no century specified
      if year < 50                        if low decade
         year = year + 2000                  assume next century
      else                                else
         year = year + 1900                  assume this century


UT2DT.PAS takes a Unix time longint on the command line, and displays the
corresponding "normal" date format.


TODAY.PAS prints out today's date as both a longint and in Unix format, along
with list of the date and time components, and a stimulating quote from one of
the undisputed shapers of modern culture.

{------------------------------------------------------------------------------}

These programs and routines are hereby released to the public domain, and I
place no restrictions on their redistribution, but I won't be held responsible
for any damage arising from the use of these routines. I have tested them to
the best of my ability and believe them to be accurate, but if you plan on
using them for any sort of critical work, you should verify for yourself that
the conversions are correct.

If you have any questions or comments about these routines, or if you find any
more bugs, please let me know. I can be reached by CompuServe email, or via the
Borland Pascal Forum (GO BPASCAL) on CompuServe, or via Delphi email, or the
Internet variants of either of these addresses, or US Mail:

   Todd Fiske   P.O. Box 9715-244      Portland Maine 04104-2000
   CompuServe   70451,1424             70451.1424@compuserve.com
   Delphi       TFISKE                 tfiske@delphi.com

